﻿using System;
using System.Linq;
using System.Web.Mvc;
using DDDSample.Application.Book;
using DDDSample.Domain.Cargo;
using DDDSample.Domain.Location;
using DDDSample.UI.Mvc.Models;

namespace DDDSample.UI.Mvc.Controllers
{
    public class BookingController : Controller
    {
        private readonly IBookService _bookService;

        public BookingController(IBookService bookService)
        {
            if (bookService == null)
            {
                throw new ArgumentNullException("bookService");
            }
            _bookService = bookService;
        }

        [HttpGet]
        public ActionResult ListCargos()
        {
            var cargos = _bookService.FindAll().Select(c => new CargoViewModel(c));
            return View(cargos);
        }

        [HttpGet]
        public ActionResult NewCargo()
        {
            AddShipingLocations();
            return View();
        }

        [HttpPost]
        public ActionResult NewCargo(string origin, string destination, DateTime? arrivalDeadline)
        {
            var validationError = false;
            if (!arrivalDeadline.HasValue)
            {
                ViewData.ModelState.AddModelError("arrivalDeadline", "Arrival deadline is required and must be a valid date.");
                validationError = true;
            }
            if (origin == destination)
            {
                ViewData.ModelState.AddModelError("destination", "Destination of a cargo must be different from its origin.");
                validationError = true;
            }
            if (validationError)
            {
                AddShipingLocations();
                return View();
            }
            var id = _bookService.BookNewCargo(new UnLocode(origin), new UnLocode(destination), arrivalDeadline.Value);
            return RedirectToDetails(id.ToString());
        }

        [HttpGet]
        public ActionResult CargoDetails(string id)
        {
            return View(GetDetailsModel(id));
        }

        [HttpGet]
        public ActionResult ChangeDestination(string id)
        {
            var cargo = _bookService.Find(Guid.Parse(id));
            var shippingLocations = _bookService.ListShippingLocations()
                .Select(x => new SelectListItem { Text = x.Name, Value = x.UnLocode.CodeString }).ToList();
            var origin = cargo.RouteSpecification.Origin.UnLocode.CodeString;
            ViewData["destination"] = shippingLocations.Where(x => x.Value != origin).ToList();
            return View(GetDetailsModel(id));
        }

        [HttpPost]
        public ActionResult ChangeDestination(string id, string destination)
        {
            _bookService.ChangeDestination(Guid.Parse(id), new UnLocode(destination));
            return RedirectToDetails(id);
        }

        [HttpGet]
        public ActionResult AssignToRoute(string id)
        {
            var cargoId = Guid.Parse(id);
            var cargo = _bookService.Find(cargoId);
            var itineraries = _bookService.RequestPossibleRoutesForCargo(cargoId);
            var assignToRouteViewModel = new AssignToRouteViewModel
            {
                Cargo = new CargoViewModel(cargo),
                RouteCandidates = itineraries.Select(x => new RouteCandidateViewModel
                {
                    Legs = x.Legs.Select(l => new LegViewModel(l)).ToList()
                }).ToList()
            };
            return View(assignToRouteViewModel);
        }

        [HttpPost]
        public ActionResult AssignToRoute(string id, RouteCandidateViewModel routeCandidate)
        {
            var itinerary = new Itinerary(routeCandidate.Legs.Select(l =>
            {
                var loadLocation = _bookService.FindLocation(new UnLocode(l.From));
                var unloadLocation = _bookService.FindLocation(new UnLocode(l.To));
                return new Leg(loadLocation, l.LoadTime, unloadLocation, l.UnloadTime);
            }));
            _bookService.AssignCargoToRoute(Guid.Parse(id), itinerary);
            return RedirectToDetails(id);
        }

        private void AddShipingLocations()
        {
            var shippingLocations = _bookService.ListShippingLocations()
                .Select(x => new SelectListItem { Text = x.Name, Value = x.UnLocode.CodeString }).ToList();
            ViewData["origin"] = shippingLocations;
            ViewData["destination"] = shippingLocations;
        }

        private ActionResult RedirectToDetails(string id)
        {
            return RedirectToAction("CargoDetails", new { id });
        }

        public CargoViewModel GetDetailsModel(string id)
        {
            var cargo = _bookService.Find(Guid.Parse(id));
            if (cargo == null)
            {
                throw new ArgumentException("Cargo with specified tracking id not found.");
            }
            return new CargoViewModel(cargo);
        }
    }
}